package com.boot2.core.service;

import com.boot2.core.dao.i.SysExceptionDAO;
import com.boot2.core.model.SysException;
import com.boot2.core.utils.CheckUtil;
import com.boot2.core.utils.DateUtil;
import com.boot2.core.utils.StringUtils;
import com.boot2.core.utils.ip.IPUtil;
import io.swagger.annotations.ApiOperation;
import lombok.extern.apachecommons.CommonsLog;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.stereotype.Service;
import org.springframework.web.method.HandlerMethod;

import java.lang.reflect.Method;
import java.util.Date;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

/**
 * Service
 *
 * @author zhangweilin
 * @version V1.0
 * @date 2016-7-15 11:32:39
 * @description 系统异常
 */
@Service
//@ConditionalOnClass({TxConfig.class})
//@Conditional(TxConditional.class) //这个也可以
@CommonsLog
@EnableAsync
public class SysExceptionService {
    @Autowired
    SysExceptionDAO sysExceptionDAO;

    public SysExceptionService() {
    }

    /**
     *
     * @param requestInfoMap
     * @param proceedingJoinPointObj
     * @param handler  后来发现proceedingJoinPointObj是可以获取到方法的,后续handler参数可省
     * @param ex
     */
    @Async
    public void sendExceptionInfo(Map<String, String>[] requestInfoMap,Object proceedingJoinPointObj, Object handler, Exception ex) {
        Map<String, String>[] infoMapArr = requestInfoMap;
        Map<String, String> headInfoMap = infoMapArr[0];
        Map<String, String> infoMap = infoMapArr[1];
        Map<String, String> cookieInfoMap = infoMapArr[2];
        SysException sysException = buildExceptionBean(infoMapArr);
        sysException.setErrorMessage(ex.getMessage());
        if (ex.getCause() != null) {
            sysException.setCauseMessage(ex.getCause().getMessage());
        }
        infoMap.putAll(headInfoMap);
        infoMap.putAll(cookieInfoMap);
        Map<String, String> loginUserInfoMap = infoMapArr[3];
        if (CheckUtil.isNotEmpty(loginUserInfoMap)) {
            infoMap.putAll(loginUserInfoMap);
        }

        String exStack = ExceptionUtils.getStackTrace(ex);
        Set<Entry<String, String>> entrySet = infoMap.entrySet();
        sysException.setStackTrace(exStack);
        sysException.setCreateTime(new Date());
        sysException.setServerIp(IPUtil.INTERNET_IP);


//        try {
////            sysExceptionDAO.saveAndFlush(sysException);
////        } catch (Exception e) {
////            log.error("保存异常信息错误",e);
////        }
//        ex.printStackTrace();
//        String exInfo = "异常id: " + sysException.getId() + "\n";
        ProceedingJoinPoint proceedingJoinPoint = null;
        HandlerMethod handlerMethod = null;
        String exInfo = "";

        if (handler != null && handler instanceof HandlerMethod&&proceedingJoinPointObj!=null&&proceedingJoinPointObj instanceof ProceedingJoinPoint ) {
            proceedingJoinPoint = (ProceedingJoinPoint) proceedingJoinPointObj;
            handlerMethod = (HandlerMethod) handler;
            if (handlerMethod != null) {
                exInfo += "\n\n调用关键信息:\n";
                exInfo += ("类:" + handlerMethod.getBeanType().getName() + "\n");
                Method method = handlerMethod.getMethod();
                exInfo += ("方法:" + method + "\n");
                exInfo += ("参数值:" + "\n");
                MethodParameter[] methodParameters = handlerMethod.getMethodParameters();
                Object[] args = proceedingJoinPoint.getArgs();
                if (null != args&&null!=methodParameters) {
                    for (int i = 0; i < methodParameters.length; i++) {
                        String parameterName = methodParameters[i].getParameterName();
                        Object value = args[i];
                        if (null != value) {
                           if (value instanceof Date) {
                                //如果是日期，格式化后输出
                                Date date = (Date) value;
                                value = DateUtil.formatDate(date);
                            }
                        }
                        exInfo += ("  参数" + (i + 1) + "("+parameterName+"): " + ArrayUtils.toString(value) + "\n");
                    }
                }
                ApiOperation[] apiOperations = method.getDeclaredAnnotationsByType(ApiOperation.class);
                if (null!=apiOperations&&apiOperations.length>0) {
                    exInfo += ("功能简介:" + apiOperations[0].value() + "\n");
                    if (StringUtils.isNotEmpty(apiOperations[0].notes())) {
                        exInfo += ("功能详细描述:" + apiOperations[0].notes() + "\n");
                    }
                }
            }
        }
        exInfo += "\n\n完整异常堆栈:\n";
        exInfo += exStack;
        exInfo += "\n\n客户端信息:\n";
        for (Entry<String, String> entry : entrySet) {
            exInfo += (entry.getKey() + ": " + entry.getValue() + "\n");
        }
        exInfo += "\n\n服务端信息:\n";

        exInfo += ("IP: " + IPUtil.INTERNET_IP + "\n");
        log.error(StringUtils.isEmpty(ex.getMessage()) ? "空异常(有可能是空指针，也有可能是空消息)"+ ", " + exInfo : ex.getMessage() + ", " + exInfo,ex);
    }

    private SysException buildExceptionBean(Map<String, String>[] infoMapArr) {
        SysException sysException = new SysException();
        Map<String, String> headInfoMap = infoMapArr[0];
        Map<String, String> cookieInfoMap = infoMapArr[2];
        Map<String, String> infoMap = infoMapArr[1];

        String headInfoStr = "";
        Set<Entry<String, String>> entrySet = headInfoMap.entrySet();
        for (Entry<String, String> entry : entrySet) {
            headInfoStr += (entry.getKey() + ": " + entry.getValue() + "\n");
        }
        sysException.setHeadInfo(headInfoStr);

        String cookieInfoStr = "";
        Set<Entry<String, String>> cookieEntrySet = cookieInfoMap.entrySet();
        for (Entry<String, String> entry : cookieEntrySet) {
            cookieInfoStr += (entry.getKey() + ": " + entry.getValue() + "\n");
        }
        sysException.setCookieInfo(cookieInfoStr);

        String method = infoMap.get("getMethod");
        String requestUri = infoMap.get("getRequestURI");
        String servletPath = infoMap.get("getServletPath");
        String serverName = infoMap.get("getServerName");
        String serverPort = infoMap.get("getServerPort");
        String remoteAddr = infoMap.get("getRemoteAddr");
        String remoteHost = infoMap.get("getRemoteHost");
        String protocol = infoMap.get("getProtocol");
        String characterEncoding = infoMap.get("getCharacterEncoding");
        Integer contentLength = Integer.valueOf(infoMap.get("getContentLength"));
        String contentType = infoMap.get("getContentType");

        String contextPath = infoMap.get("getContextPath");
        String localAddr = infoMap.get("getLocalAddr");
        String localName = infoMap.get("getLocalName");
        Integer localPort = Integer.valueOf(infoMap.get("getLocalPort"));
        String pathInfo = infoMap.get("getPathInfo");
        String pathTranslated = infoMap.get("getPathTranslated");
        String queryString = infoMap.get("getQueryString");
        Integer remotePort = Integer.valueOf(infoMap.get("getRemotePort"));
        String remoteUser = infoMap.get("getRemoteUser");
        String requestedSessionId = infoMap.get("getRequestedSessionId");
        String requestUrl = infoMap.get("getRequestURL");
        String scheme = infoMap.get("getScheme");
        String servletContextPath = infoMap.get("getServletContext.getContextPath");
        String sessionId = infoMap.get("getSession.getId");
        String userPrincipal = infoMap.get("getUserPrincipal");
        String lastPath = infoMap.get("getLastPath");
        String fullUrl = infoMap.get("getFullUrl");
        String ip = infoMap.get("getIp");

        sysException.setMethod(method);
        sysException.setRequestUri(requestUri);
        sysException.setServletPath(servletPath);
        sysException.setServerName(serverName);
        sysException.setServerPort(serverPort);
        sysException.setRemoteAddr(remoteAddr);
        sysException.setRemoteHost(remoteHost);
        sysException.setProtocol(protocol);
        sysException.setCharacterEncoding(characterEncoding);
        sysException.setContentLength(contentLength);
        sysException.setContentType(contentType);

        sysException.setContextPath(contextPath);
        sysException.setLocalAddr(localAddr);
        sysException.setLocalName(localName);
        sysException.setLocalPort(localPort);
        sysException.setPathInfo(pathInfo);
        sysException.setPathTranslated(pathTranslated);
        sysException.setQueryString(queryString);
        sysException.setRemotePort(remotePort);
        sysException.setRemoteUser(remoteUser);
        sysException.setRequestedSessionId(requestedSessionId);
        sysException.setRequestUrl(requestUrl);
        sysException.setScheme(scheme);
        sysException.setServletContextPath(servletContextPath);
        sysException.setSessionId(sessionId);
        sysException.setUserPrincipal(userPrincipal);
        sysException.setLastPath(lastPath);
        sysException.setFullUrl(fullUrl);
        sysException.setIp(ip);

        return sysException;
    }
}
